package com.rockwell.sniffyhunter.model;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;

import android.annotation.SuppressLint;

import com.rockwell.sniffyhunter.utils.Vector2D;

@SuppressLint("UseSparseArrays")
public class NorthernStarFinder {
    private int m_zone;
    private Vector2D m_vector;
    private ArrayList<Vector2D> m_obstacleList;
    private ArrayList<Vector2D> m_discoveredMaze;
    private ArrayList<Vector2D> m_unpickedTreasureList;
    private HashSet<Integer> m_discoveredZone;

    // TODO: Assuming 5x10 playing field. 
    // Zone starts from 1-50. 
    // Coordinates starts from (0,0) to (9,4)
    @SuppressWarnings("serial")
	private static final Map<Integer, Vector2D> ZONE_MAP = new HashMap<Integer, Vector2D>() {
	{
    	//Map<Integer, Vector2D> zone = new HashMap<Integer, Vector2D>();
    	put( 1, new Vector2D(0, 0,   0));
    	put( 2, new Vector2D(0, 1,   0));
    	put( 3, new Vector2D(0, 2,   0));
    	put( 4, new Vector2D(0, 3,   0));
    	put( 5, new Vector2D(0, 4,   0));
    	put( 6, new Vector2D(1, 4,  90));
    	put( 7, new Vector2D(1, 3, 180));
    	put( 8, new Vector2D(1, 2, 180));
    	put( 9, new Vector2D(1, 1, 180));
    	put(10, new Vector2D(1, 0, 180));
    	
    	put(11, new Vector2D(2, 0,  90));
    	put(12, new Vector2D(2, 1,   0));
    	put(13, new Vector2D(2, 2,   0));
    	put(14, new Vector2D(2, 3,   0));
    	put(15, new Vector2D(2, 4,   0));

    	put(16, new Vector2D(3, 4,  90));
    	put(17, new Vector2D(3, 3, 180));
    	put(18, new Vector2D(3, 2, 180));
    	put(19, new Vector2D(3, 1, 180));
    	put(20, new Vector2D(3, 0, 180));
    	
    	put(21, new Vector2D(4, 0,  90));
    	put(22, new Vector2D(4, 1,   0));
    	put(23, new Vector2D(4, 2,   0));
    	put(24, new Vector2D(4, 3,   0));
    	put(25, new Vector2D(4, 4,   0));

    	put(26, new Vector2D(5, 4,  90));
    	put(27, new Vector2D(5, 3, 180));
    	put(28, new Vector2D(5, 2, 180));
    	put(29, new Vector2D(5, 1, 180));
    	put(30, new Vector2D(5, 0, 180));
    	
    	put(31, new Vector2D(6, 0,   0));
    	put(32, new Vector2D(6, 1,   0));
    	put(33, new Vector2D(6, 2,   0));
    	put(34, new Vector2D(6, 3,   0));
    	put(35, new Vector2D(6, 4,   0));
    	
    	put(36, new Vector2D(7, 4,  90));
    	put(37, new Vector2D(7, 3, 180));
    	put(38, new Vector2D(7, 2, 180));
    	put(39, new Vector2D(7, 1, 180));
    	put(40, new Vector2D(7, 0, 180));
    	
    	put(41, new Vector2D(8, 0,  90));
    	put(42, new Vector2D(8, 1,   0));
    	put(43, new Vector2D(8, 2,   0));
    	put(44, new Vector2D(8, 3,   0));
    	put(45, new Vector2D(8, 4,   0));

    	put(46, new Vector2D(9, 4,  90));
    	put(47, new Vector2D(9, 3, 180));
    	put(48, new Vector2D(9, 2, 180));
    	put(49, new Vector2D(9, 1, 180));
    	put(50, new Vector2D(9, 0, 180));
    	//ZONE_MAP = Collections.unmodifiableMap(zone);
	}
    };
    
    public NorthernStarFinder() {
    	this(1);
    }
    
    public NorthernStarFinder(int startZone) {
    	m_zone = startZone;
    	m_vector = GetCurrentZoneInfo();
    	
     	m_obstacleList = new ArrayList<Vector2D>();
     	m_discoveredMaze = new ArrayList<Vector2D>();
     	m_discoveredZone = new HashSet<Integer>();
    }
    
    public void Rotate(int angle) {
    	m_vector.angleFromNorth += angle;
  	
    	// Convert it to base angle
    	int nRepetition = m_vector.angleFromNorth / 360;
    	if (m_vector.angleFromNorth >= 0) {
    		m_vector.angleFromNorth -= (nRepetition * 360);
    	}
    	else {    		
    		m_vector.angleFromNorth += (nRepetition + 1) * 360;
    	}
    }

	public void MapDiscoveredObstacle() {
        m_obstacleList.add(m_vector.clone());
    }

    public void MapDiscoveredMaze() {
        m_discoveredMaze.add(m_vector.clone());
    }
    
    public void MapUnpickedTreasure() {
    	m_unpickedTreasureList.add(m_vector.clone());
    }

    public void MoveForward(double d) {
        float dx = (float) (d * Math.sin(Math.toRadians(m_vector.angleFromNorth)));
        float dy = (float) (d * Math.cos(Math.toRadians(m_vector.angleFromNorth)));
        m_vector.coord.offset(dx ,dy);
    }

    public void MapDiscoveredZone() {
        m_discoveredZone.add(m_zone);
        m_zone++;
    }

    public Vector2D GetCurrentZoneInfo() {
    	return ZONE_MAP.get(m_zone).clone();
    }

	public Vector2D WhereAmI() {
		return m_vector.clone();
	}
	
	public Vector2D FindNearestObstacle() {
		return FindNearestPoint(m_obstacleList);
	}
	
	public Vector2D FindNearestMaze() {
		return FindNearestPoint(m_discoveredMaze);
	}
	
	public Vector2D FindNearestUnpickedTreasure() {
		return FindNearestPoint(m_unpickedTreasureList);
	}
	
	private Vector2D FindNearestPoint(ArrayList<Vector2D> pointList) {
		Vector2D nearestPoint = new Vector2D();
		double shortestDistance = Double.MAX_VALUE;
		double distance = 0;
		
		for (Vector2D point : pointList) {
			distance = Vector2D.calculateDistanceBetween(m_vector, point);
			if (distance < shortestDistance) {
				shortestDistance = distance;
				nearestPoint = point.clone();
			}
		}
		
		return (distance == Double.MAX_VALUE) ? null : nearestPoint;
	}
}